<?php
// 手册地址  http://www.php.net/manual/zh/keyword.class.php
/**
 * 每个类的定义都以关键字 class 开头，后面跟着类名，可以是任何非 PHP 保留字的名字。后面跟着一对花括号，
 * 里面包含有类成员和方法的定义。
 * 伪变量 $this 可以在当一个方法在对象内部调用时使用。
 * $this 是一个到调用对象（通常是方法所属于的对象，但也可以是另一个对象，如果该方法是从第二个对象内静态调用的话）的引用。
 *
 * 类的组成
 *
 * 类名+成员方法+成员变量
 *
 * 成员方法的属性 public private protected
 * public 公共的
 *
 * private 私有的
 *
 * protected 保护的
 */

class Class1
{
    public $param = "我是Class1的成员对象\n";
    public function test()
    {
        return "test调用：" . $this->param;
    }
}

$class1 = new Class1();
$class1->param = 'sdfasdf';
echo $class1->param;
echo $class1->test();

class Class2
{
    private $param = "我是Class1的成员对象\n";
    public function test()
    {
        return "test调用：" . $this->param;
    }
    // 更改private(私有的)和protected(受保护的)成员变量里。可以写一个public方法 
    public function setparam($value='')
    {
        $this->param = $value;
    }
}

$class2 = new Class2();
echo $class2->param;
// 这里会报错 因为成员变量$param是私有滴
echo $class2->test();
// 更改private(私有的)和protected(受保护的)成员变量用法 
$class2->setparam('dddddddddddd');


class Class3
{
    protected $param = "我是Class1的成员对象\n";
    public $param2   = "我是Param2，公共的";
    private function test()
    {
        return "test调用：" . $this->param;
    }
}

$class3 = new Class3();
echo $class3->param;
//这里会报错，因为成员变量$param受保护的，不能在外部调用。
echo $class3->test();
//这里会报错，因为成员方法test是私有的。

class Class4 extends Class3
{
    protected $param3 = "param3";
    public function test2()
    {
        $this->test();
        //这里是报错,因为CLass4是继承Class3，在Class3里面成员方法test是私有的。
        $this->param;
        //这里是正确的，因为Class3里面的成员变量$param保护的，可继承的。
        //
        $this->param2;
        //这里是正确的，因为CLass3里面的成员变量$param2是公共的、可调用的。
    }
}

$class4 = new Class4();

/*
 * 结论：
 * 属性                               类型                      是否可继承                           是否可被外部调用
 * protected                        受保护的                        是的                              不能被外部调用
 * public                           公共的                          是的                               能被外部调用
 * private                          私有的                          不能                              不能被外部调用
 *
 

*
 * 方法重写
 * 重写/覆盖  override  
 * 指:子类重写了父类的同名方法
 * 方法重写时方法属性必须遵循下面的开放程度。
 * private < protected < public
 * 只能越来越开放。
*/
 

class Class5
{

    protected function test($value = '')
    {
        echo 'Class5';
    }

    public function test1($value='')
    {
        # code...
    }

    private function test2($value='')
    {
        # code...
    }
}
// 这个方法是 错误 的 因为父类中这个方法的属性是 XX 重写后应该设置为 XX 或者 XX
    // 这个方法是 正确 的 因为父类中这个方法的属性是 XX 重写后可以设置为 XX 或者 XX
class Class6 extends Class5
{
    // 这个方法是错误的，因为父类中这个方法的属性是protected（受保护的）重写后应该设置为protected（书保护的）或者public（公共的）
    private function test($value='')
    {//

        echo 'Class6';
    }
·   //这个方法是错误的，因为父类中这个方法的属性是public（公共的）重写后应该设置为public（公共的）
    protected function test1($value='')
    {
        # code...
    }
    //这个方法是正确的这个方法的属性是private（私有的）重写后应该设置为private（私有的）、protected（受保护的）、public（公共的）
    private function test2($value='')
    {
        # code...
    }
}

class Class7 extends Class5
{
    // 这个方法是正确的，因为父类中这个方法的属性是protected（受保护的）重写后应该设置为protected（受保护的）、public（公共的）
    protected function test($value='')
    {
        echo 'Class7';
    }
}

class Class8 extends Class5
{
    // 这个方法是正确的，因为父类中对这个方法的属性是pprotected（受保护的）再重写后应该设置为protected（受保护的）、public（公共的）
    public function test($value='')
    {
        echo 'Class8';
    }
}

/**
 * abstract 抽象类
 *
 * 抽象类不可以被实例化 必需被继承才可以实例化
 * 抽象类中 至少要包含一个抽象方法。如果类方法被声明为抽象的，那么其中就不能包括具体的功能实现。
 * 该抽象方法必需在子类中重新定义
 */

abstract class Class9
{
    public $param = "最后";
    abstract public function test();
}

class Class10 extends Class9
{
    public function test()
    {
        # code...
    }
}

/**
 * 静态方法
 * static关键字用来修饰属性、方法，称这些属性、方法为静态属性、静态方法。
 */

class Class11
{
    public static $param = "dsfasdfasd";
    public static function test()
    {
        return self::$param;
    }
}

Class11::$param = 'ddddd';
echo Class11::test();

/**
 * 类的魔术方法
 */

class Class12
{
    protected $param = 'mysql';

    protected $config = array('test'=>1);
    /**
     * 构造函数
     * 对象在实例化(new)时，会被调用的方法
     */
    public function __construct($param)
    {
        $this->param = $param;
    }

    /**
     * 外部调用时。对象没有这个变量会调用__get方法 格式 是 __get('变量名');
     * @param  [type] $name [description]
     * @return [type]       [description]
     */
    public function __get($name)
    {
        return $this->config[$name];
    }

    /**
     * 外部设置时。对象没有这个变量会调用__set方法 格式 是 __set('变量名','赋值参数');
     * @param [type] $name  [description]
     * @param [type] $value [description]
     */
    public function __set($name, $value)
    {
        $this->config[$name] = $value;
    }

    /**
     * 外部调用时。对象没有这个方法会调用__call方法 格式 是 __call(方法名, 参数), 参数是经数组的形式传过来
     * @param [type] $value [description]
     */
    public function __call($function,$arguments)
    {
        $function = "A_".$function;

        $this->{$function}($arguments[0]);
    }
    /**
     * 用于__call示例
     * @param [type] $value [description]
     */
    protected function A_test($value)
    {
        echo $value;
    }
}

$class12 = new Class12('mysqli');
// 对象在实例化时会去调用 __construct 函数

$class12->test;
// 等同于 $class12->__get('test');

$class12->test = 'asdfasd';
// 等同于 $class12->__set('test', 'asdfasd');

$class12->test('aaa');
// 等同于 $class12->__call('test', array('aaa'));


